
from Crypto.Cipher import AES
from Crypto.Hash import CMAC
from binascii import hexlify, unhexlify
import os
import sys
import logging
logging.getLogger().setLevel(logging.INFO)
curPath = os.path.abspath(os.path.dirname(__file__))
rootPath = os.path.abspath(os.path.dirname(curPath))
sys.path.append(rootPath)
sys.path.append(os.path.join(rootPath,'commDriver'))
sys.path.append(os.path.join(rootPath,'data'))


from commDriver.TS_CAN import *
import pytest
from pytest_assume.plugin import assume
import crcmod
import math
import threading
from Crypto.Cipher import AES
from eprogress import LineProgress
from threading import Thread
import hashlib
import xml.etree.ElementTree as ET
import zipfile
import shutil
import Set_up

class Download(Thread):
    # def __init__(self,candriver,diag,diagfun,zip_path,erash_startaddress,erash_size,KeyKValue,exit=False,crcerror=False,skipprecheck=False,skipflash=False):
    def __init__(self,candriver,diag,diagfun,zip_path,erash_startaddress,erash_size,exit=False,crcerror=False,skipprecheck=False,skipflash=False, skipboot=False):
        Thread.__init__(self)
        self.candriver = candriver          #can引用
        # self.candr=candrr
        self.diag = diag                    #诊断引用--物理寻址
        self.diagfun = diagfun              #诊断引用--功能寻址
        self.zip_path = zip_path            #zip文件路径
        self.eras_startaddress=erash_startaddress           #31服务擦除起始地址
        self.erash_size=erash_size          #31服务擦除地址大小
        self.startaddress=0x0               #bin文件刷写起始地址
        self.size=0x0                       #bin文件刷写大小
        self.stop_flag=False                #外部条件停止刷写过程
        self.progress_number = 0            #刷写进度条
        self.md5_state = False              #MD5文件读取比对正常标志
        self.success_flag = False           #刷写成功标志
        self.app = False                    #app/flash选择
        self.normal = False                 #刷写异常后能否正常开机
        self.erashing = False               #正在擦除内存标志
        self.exit = exit                    #退出当前boot模式
        self.crcerror=crcerror              #crc校验错误
        self.skipprecheck=skipprecheck      #跳过预刷写条件检测
        self.skipflash=skipflash            #跳过Flash Driver下载
        self.finish = False                 #刷写完成
        self.AppSize = 0
        self.BootSize = 0
        self.skipboot=skipboot          #跳过boot模式下载
        
        # self.KeyKValue = KeyKValue
        self.start()

    def get_path(self):
        root = os.path.abspath(os.path.dirname(__file__))
        dirpath = os.path.join(root,r'temp')
        zFile = zipfile.ZipFile(self.zip_path, "r")
        self.app_filepath = ''
        self.app_filename = ''
        self.flash_filepath = ''
        self.flash_filename = ''
        self.xml_filepath = ''
        self.xml_filename = ''
        self.boot_name = ''
        self.boot_filepath = ''
        for fileM in zFile.namelist(): 
            zFile.extract(fileM, dirpath)
            if 'app' in fileM.lower():
                self.app_filename=fileM
                self.app_filepath = os.path.join(dirpath,fileM)
            elif 'flash' in fileM.lower():
                self.flash_filename=fileM
                self.flash_filepath = os.path.join(dirpath,fileM)
            elif 'xml' in fileM.lower():
                self.xml_filename=fileM
                self.xml_filepath = os.path.join(dirpath,fileM)
            elif 'boot' in fileM.lower():
                self.boot_name=fileM
                self.boot_filepath = os.path.join(dirpath,fileM)
            else:
                pass

        zFile.close()
        return self.app_filepath,self.flash_filepath,self.xml_filepath,self.boot_filepath
    def parse_file(self,file_path):
        logging.info(f'file_path:{file_path}')
        self.file_type = os.path.splitext(file_path)[-1]
        self.binfile_handle = bincopy.BinFile()
        if(self.file_type == '.hex' or self.file_type == '.HEX'):
            with open(file_path, 'r') as fin:
                self.binfile_handle.add_ihex(fin.read())
            self.size = self.binfile_handle.segments.maximum_address - self.binfile_handle.segments.minimum_address
        if(self.file_type == '.bin' or self.file_type == '.BIN'):
            with open(file_path, 'rb') as fin:
                self.binfile_handle.add_binary(fin.read())
        if(self.file_type == '.s19'):
            with open(file_path, 'r') as fin:
                self.binfile_handle.add_srec(fin.read())
        # logging.info(binfile_handle.segments())  #All data
        return self.binfile_handle.segments   # 用segments可以查看里面的每个数据段内容

    def download(self,path):
        download_data=[]
        self.parse_file(path)
        for index,segment in enumerate(self.binfile_handle.segments):
            list_segment_data= list(segment.data)
            # logging.info(f'segment.data:{list_segment_data}')
            
            logging.info(f'segment.data[0]:{list_segment_data[0]}')
            if(self.file_type == '.bin' or self.file_type == '.BIN'):
                start_address = self.startaddress
                segment_size = self.size
            else:
                start_address = segment.minimum_address
                segment_size = segment.maximum_address - segment.minimum_address
                
            # 34 Server,Request download
            Request_download_CMD = [0x34,00,0x44] + \
                                    [(start_address>>24) & 0xFF] + [(start_address>>16) & 0xFF] + [(start_address>>8) & 0xFF] + [(start_address) & 0xFF] +\
                                    [(segment_size>>24) & 0xFF] + [(segment_size>>16) & 0xFF] + [(segment_size>>8) & 0xFF] + [(segment_size) & 0xFF]
            rep_value,rep_size = self.diag.req_and_res_can(Request_download_CMD,len(Request_download_CMD),enable_print= False)

            if rep_value[0:1] == [0x74]:
                lengthformatidentifier = rep_value[1]
                maxnumberofblocklength = rep_value[2]*256+rep_value[3]
                # 36 Server, start download
                if maxnumberofblocklength<0xfff:
                    numberofblockdatalength = maxnumberofblocklength-2
                    data_36_send_times = math.ceil(segment_size/numberofblockdatalength)
                    print('data_36 send times is: ',data_36_send_times)
                    # line progress
                    # line_progress = LineProgress(title='upgrade progress')
                    try:
                        for i in range(data_36_send_times):
                            self.progress_number = ((i+1)/data_36_send_times)*100
                            # line_progress.update(self.progress_number)
                            blocksequencecounter = (i+1)%256
                            if i == (data_36_send_times - 1):
                                download_data_CMD = [0x36,blocksequencecounter]+list(segment.data)[i*numberofblockdatalength:]
                            else:
                                download_data_CMD = [0x36,blocksequencecounter]+list(segment.data)[i*numberofblockdatalength:((i+1)*numberofblockdatalength)]
                            rep_value,rep_size = self.diag.req_and_res_can(download_data_CMD,len(download_data_CMD),enable_print= False)
                            # logging.info('i is: {}, rep_value is: {}, flag is: {}'.format(i,rep_value,self.stop_flag))
                            if rep_value[0:1] != [0x76] or self.stop_flag == True :
                                self.success_flag = False
                                raise Exception('download termination')

                    except Exception as e:
                        logging.info(e)
            else:
                self.success_flag = False
                logging.info('34 Server negative response!')
            # 37 Server, stop download
            # citt=crcmod.predefined.mkCrcFun('crc-ccitt-false')
            # download_data = bytearray(list(segment.data))
            # crc = citt(download_data)



            download_data = download_data + list(segment.data) 
            logging.info(f'download_data:{len(download_data)}')
            try:
                rep_value,rep_size = self.diag.req_and_res_can([0x37],1,enable_print= False)
                if rep_value[0:1] == [0x77]:
                    logging.info('Block {} upgrade success!'.format(index))
                else:
                    logging.info('37 server negative response!')
            except:
                logging.info('37 server no response!')
        # crc_32=crcmod.predefined.mkCrcFun('crc-32')
        # download_data_byte = bytearray(download_data)
        # crc_value = binascii.crc32(download_data_byte)

        citt=crcmod.predefined.mkCrcFun('jamcrc')
        download_data_byte = bytearray(download_data)
        # download_data = bytearray(list(segment.data))
        crc_value = citt(download_data_byte)


        # crc_32=crcmod.predefined.mkCrcFun('crc-32')
        # download_data_byte = bytearray(download_data)
        # crc_value = binascii.crc32(download_data_byte)  # CRC32  多项式：0x4C11DB7
        return crc_value

    def pad(self, text):
        """
        #填充函数,使被加密数据的字节码长度是block_size的整数倍
        #AES128-ECB PKCS5填充,补齐到16字节,补充12个0xc
        """
        s = ''
        for i in text:
            if i > 15:
                s += hex(i)[2:] # 将seed转化成'6816b4d5'
            else:
                s += '0'
                s += hex(i)[2:]
        logging.info(f's:{s}')
        count = len(text) # seed 固定为4
        add = self.length - (count % self.length) # 所以需要填充12个
        # entext = s + (chr(add) * add)
        entext = s + ('0c'*add)
        return entext

    def seedkey(self,seed,level=1):
        #BE12
        logging.info('seedkey')
        seed = bytearray(seed)

        mainkey = unhexlify('01020304050607080102030405060708')
        df = unhexlify('1F9D6DC2CCF3E59C160081E64E2D8899')
        sk = CMAC.new(mainkey,df,ciphermod=AES)
        sk = unhexlify(sk.hexdigest())
        
        token = CMAC.new(sk,seed,ciphermod=AES)
        logging.info('1F9D6DC2CCF3E59C160081E64E2D8899'+token)
        token = list(unhexlify(token.hexdigest()))       
        key = list(df)+token
        return key

    def vechile_message(self,flag):
            self.candriver.Enable_rbs_message(Set_up.NetName,Set_up.nodeName,'PBM_0x185',Enalbe=flag)
            self.candriver.Enable_rbs_message(Set_up.NetName,Set_up.nodeName,'SAS_1',Enalbe=flag)
            self.candriver.Enable_rbs_message(Set_up.NetName,Set_up.nodeName,'WCBS_ESP_4',Enalbe=flag)
            self.candriver.Enable_rbs_message(Set_up.NetName,Set_up.nodeName,'EMS_5',Enalbe=flag)
            self.candriver.Enable_rbs_message(Set_up.NetName,Set_up.nodeName,'APA_2',Enalbe=flag)
            self.candriver.Enable_rbs_message(Set_up.NetName,Set_up.nodeName,'CDC_IPC1',Enalbe=flag)
            self.candriver.Enable_rbs_message(Set_up.NetName,Set_up.nodeName,'WCBS_ESP_1',Enalbe=flag)
            self.candriver.Enable_rbs_message(Set_up.NetName,Set_up.nodeName,'VCU_PDCU_0x230',Enalbe=flag)
            self.candriver.Enable_rbs_message(Set_up.NetName,Set_up.nodeName,'WCBS_ESP_6',Enalbe=flag)
            self.candriver.Enable_rbs_message(Set_up.NetName,Set_up.nodeName,'WCBS_ESP_3',Enalbe=flag)
            self.candriver.Enable_rbs_message(Set_up.NetName,Set_up.nodeName,'BCM_0x2F1',Enalbe=flag)
            self.candriver.Enable_rbs_message(Set_up.NetName,Set_up.nodeName,'PEPS_0x1B1',Enalbe=flag)
            self.candriver.Enable_rbs_message(Set_up.NetName,Set_up.nodeName,'WCBS_ESP_10',Enalbe=flag)
            self.candriver.Enable_rbs_message(Set_up.NetName,Set_up.nodeName,'WCBS_ESP_13',Enalbe=flag)
            self.candriver.Enable_rbs_message(Set_up.NetName,Set_up.nodeName,'BDC_BCM_9',Enalbe=flag)
            self.candriver.Enable_rbs_message(Set_up.NetName,Set_up.nodeName,'BDC_BCM_2',Enalbe=flag)
            self.candriver.Enable_rbs_message(Set_up.NetName,Set_up.nodeName,'CDC_EHC_6',Enalbe=flag)
            self.candriver.Enable_rbs_message(Set_up.NetName,Set_up.nodeName,'CDC_EHC_3',Enalbe=flag)
            self.candriver.Enable_rbs_message(Set_up.NetName,Set_up.nodeName,'CDC_EHC_24',Enalbe=flag)
            self.candriver.Enable_rbs_message(Set_up.NetName,Set_up.nodeName,'TIM_1',Enalbe=flag)

    def pre_update(self):
        self.vechile_message(False)  # 停发车身报文
        try:
            '''预刷新步骤'''
            #step1: 扩展会话模式            ：软件更新工具用的物理寻址
            uds_rep,datasize = self.diagfun.req_and_res_can([0x10,0x03],2)  # diagfun
            with assume: assert uds_rep[0:2] == [0x50,0x03]
            if uds_rep[0:2] == [0x50,0x03]:
                logging.info('Enter extended session ok')
            else:
                logging.info('Enter extended session fail')
            # self.can1 = self.candriver.send_CANFrame_Cycle(0x7DF,[0x02,0x3E,0x80,0xCC,0xCC,0xCC,0xCC,0xCC],4000,'CANFD')
            #step2: 检查预刷新条件
            if self.skipprecheck == False:
                uds_rep,datasize = self.diag.req_and_res_can([0x31,0x01,0x02,0x03],4)
                with assume: assert uds_rep[0:4] == [0x71,0x01,0x02,0x03]
                if uds_rep[0:4] == [0x71,0x01,0x02,0x03]:
                    logging.info('Check programming precondition ok')
                else:
                    logging.info('Check programming precondition fail')
                #step3: 停止诊断服务
                uds_rep,datasize = self.diagfun.req_and_res_can([0x85,0x02],2)  # diagfun
                with assume: assert uds_rep[0:2] == [0xC5,0x02]
                if uds_rep[0:2] == [0xC5,0x02]:
                    print('Disable DTC ok')
                else:
                    print('Disable DTC fail')
                #step4: 通信使能接受，关闭发送
                self.ab = self.candriver.send_CANFrame_Cycle(0x7DF,[0x02,0x3E,0x80,0xCC,0xCC,0xCC,0xCC,0xCC],1000,'CANFD')
                uds_rep,datasize = self.diagfun.req_and_res_can([0x28,0x03,0x01],3) # diagfun
                # self.vechile_message(False)  # 停发车身报文
                with assume: assert uds_rep[0:2] == [0x68,0x03]
                if uds_rep[0:2] == [0x68,0x03]:
                    print('Communication send disable and receive enable ok')
                else:
                    print('Communication send disable and receive enable fail')
                # #step5: 根据标识符读取数据服务           
                # uds_rep,datasize = self.diag.req_and_res_can([0x22,0xF1,0x83],3)
                # if uds_rep[0:1] == [0x62]:
                #     print('VIN is: {}'.format(uds_rep))
                # else:
                #     print('Read VIN fail')
                # uds_rep,datasize = self.diag.req_and_res_can([0x22,0xF1,0x91],3)
                # if uds_rep[0:1] == [0x62]:
                #     print('ECU Hardware Number is: {}'.format(uds_rep))
                # else:
                #     print('Read ECU Hardware Number fail')
                # uds_rep,datasize = self.diag.req_and_res_can([0x22,0xF1,0x87],3)
                # if uds_rep[0:1] == [0x62]:
                #     print('ECU Part Number is: {}'.format(uds_rep))
                # else:
                #     print('Read ECU Part Number fail')
                '''主刷新步骤'''
                #step6: 编程会话模式
                uds_rep,datasize = self.diag.req_and_res_can([0x10,0x02],2)
                with assume: assert uds_rep[0:2] == [0x50,0x02]
                if uds_rep[0:2] == [0x50,0x02]:
                    print('Enter programming session ok')
                else:
                    print('Enter programming session fail')
                time.sleep(1)
                #step7: 安全算法
                uds_rep,datasize = self.diag.req_and_res_can([0x27,0x11],2)
                with assume: assert uds_rep[0:2] == [0x67,0x11]
                if uds_rep[0:2] == [0x67,0x11]:
                    print('Request seed ok')
                else:
                    print('Request seed fail')
                seed = uds_rep[2:]
                
                key = self.seedkey(seed,3)
                uds_rep,datasize = self.diag.req_and_res_can([0x27,0x12]+key,2 + len(key))
                with assume: assert uds_rep[0:2] == [0x67,0x12]
                if uds_rep[0:2] == [0x67,0x12]:
                    print('Send key level3 ok')
                else:
                    print('Send key level3 fail')

                #step8: 按标识符写入数据$2E $F184
                uds_rep,datasize = self.diag.req_and_res_can([0x2E,0xF1,0x84]+Set_up.ApplicationSoftwareFingerprint,12)
                with assume: assert uds_rep[0:3] == [0x6E,0xF1,0x84]
                if uds_rep[0:3] == [0x6E,0xF1,0x84]:
                    print('Write ApplicationSoftwareFingerprint ok')
                else:
                    print('Write  ApplicationSoftwareFingerprint fail')
                if self.exit:
                    #step9-1: 退出boot模式
                    uds_rep,datasize = self.diag.req_and_res_can([0x10,0x01],2)
                    with assume: assert uds_rep[0:2] == [0x50,0x01]
                    if uds_rep[0:2] == [0x50,0x01]:
                        logging.info('Exit programming session ok')
                    else:
                        logging.info('Exit programming session fail')
            else:
                logging.info('Berfor update, CAN Communication Error!')
        except:
            logging.info('Berfor update, CAN Communication Error!')

    def getFile(self,filepath):
        segments = self.parse_file(filepath)
        if(self.file_type == '.bin' or self.file_type == '.BIN'):
                start_address = self.startaddress
                segment_size = self.size
        else:
            start_address = segments.minimum_address
            segment_size = segments.maximum_address - segments.minimum_address
            logging.info(f"start_address,segment_size:{start_address}")
        return start_address, segment_size

    def flash_driver_update(self):
        #step:10/11/12: 34、36、37服务更新flash Drive   
        check_crc_flash = self.download(self.flash_filepath) 
        logging.info(f'check_crc_flash:{check_crc_flash}')  
        #step13: 检查传输行列
        crc_flash=[0,0,0,0]
        crc_flash[0] = ((check_crc_flash)>>24) & 0xFF
        crc_flash[1] = ((check_crc_flash)>>16) & 0xFF
        crc_flash[2] = ((check_crc_flash)>>8) & 0xFF
        crc_flash[3] = (check_crc_flash) & 0xFF
        try:
            # uds_rep,datasize = self.diag.req_and_res_can([0x31,0x01,0x02,0x02]+[0x01,0x01,0x02,0x01],8)
            uds_rep,datasize = self.diagfun.req_and_res_can([0x3E,0x80],2)
            uds_rep,datasize = self.diag.req_and_res_can([0x31,0x01,0x02,0x02]+crc_flash,8)
            if uds_rep[0:5] == [0x71,0x01,0x02,0x02,0x00]:
                logging.info('Check Flash transmit sequence ok')
            else:
                logging.info('Check Flash transmit sequence fail')
        except:
            logging.info('31 server check Flash transmit sequence no response!')
			
    def app_update(self):
        ##step9:更新flash Drive
        # if self.skipflash == False:
        #     self.flash_driver_update()

        # #step8: 34、36、37服务更新flash Drive         ：软件刷写工具无此步骤
        # flash_filepath = r'C:\Users\test\Desktop\andy\boot-test\firmware_combine.bin'
        # download_data,data_37 = self.download(self.diag,flash_filepath,startaddress,size)     #bin文件起始地址和数据长度
        # citt = crcmod.predefined.mkCrcFun('crc-ccitt-false')
        # crcdata = hex(citt(bytearray(download_data)))
        # #crcdata == data_37[2:4]
        # #step9: 写指纹信息(文档说是刷新安全级-2)
        # PIF_data = [0x43,0x12,0x34,0x56,0x78,0x1A,0x2B,0x3C,0x4D,0x5E,0x6F,0x00,0x00,0x00,0xFF,0xFF]
        # uds_rep,datasize = self.diag.req_and_res_can([0x2E,0xF1,0x10]+PIF_data,19)
        # if uds_rep[0:1] == [0x6E]:
        #     print('Write PIF ok')
        # else:
        #     print('Write PIF fail')
        self.app = True
        '''内存擦除前'''
        time.sleep(1)
        #step14: 擦除存储器程序
        self.erashing = True
        time.sleep(0.5)
        '''内存擦除中'''
        try:
            startaddress, size = self.getFile(self.app_filepath)
            eras_startaddress = [0x00,0x00,0x00,0x00]
            eras_startaddress[0] = ((startaddress)>>24) & 0xFF
            eras_startaddress[1] = ((startaddress)>>16) & 0xFF
            eras_startaddress[2] = ((startaddress)>>8) & 0xFF
            eras_startaddress[3] = (startaddress) & 0xFF
            erash_size = [0x00,0x00,0x00,0x00]
            erash_size[0] = ((size)>>24) & 0xFF
            erash_size[1] = ((size)>>16) & 0xFF
            erash_size[2] = ((size)>>8) & 0xFF
            erash_size[3] = (size) & 0xFF


            uds_rep,_ = self.diag.req_and_res_can([0x31,0x01,0xFF,0x00,0x44] + eras_startaddress + erash_size,13)
            with assume: assert uds_rep[0:5] == [0x71,0x01,0xFF,0x00,0x00]
            if uds_rep[0:5] == [0x71,0x01,0xFF,0x00,0x00]:
                logging.info('Erase Memory ok')
            else:
                logging.info('Erase Memory fail')
                return
        except:
            with assume: assert 1 == 0
            logging.info('Erase Memory no response!')
        time.sleep(1)

        #step15/16/17: 34、36、37服务下载程序
        self.erashing = False
        check_crc_app = self.download(self.app_filepath) 
        logging.info(f'check_crc_app:{check_crc_app}')  
        #step18: 检查传输行列
        if self.crcerror:
            crc_app=[0,0,0,0]
        else:
            crc_app=[0,0,0,0]
            crc_app[0] = ((check_crc_app)>>24) & 0xFF
            crc_app[1] = ((check_crc_app)>>16) & 0xFF
            crc_app[2] = ((check_crc_app)>>8) & 0xFF
            crc_app[3] = (check_crc_app) & 0xFF
        try:
            # uds_rep,datasize = self.diag.req_and_res_can([0x31,0x01,0x02,0x02]+[0x01,0x01,0x02,0x01],8)
            uds_rep,datasize = self.diagfun.req_and_res_can([0x3E,0x80],2)
            uds_rep,datasize = self.diag.req_and_res_can([0x31,0x01,0x02,0x02]+crc_app,8)
            with assume: assert uds_rep[0:5] == [0x71,0x01,0x02,0x02,0x00]
            if uds_rep[0:5] == [0x71,0x01,0x02,0x02,0x00]:
                logging.info('Check APP transmit sequence ok')
            else:
                logging.info('Check APP transmit sequence fail')
                with assume: assert 1 == 0
                # logging.info('crc value is response: '.format(hex(crc_app)))
                # logging.info('31 response: '.format(hex(uds_rep)))   
        except:
            logging.info('Check APP transmit and programming Dependencies no response!')
            with assume: assert 1 == 0
            self.finish = False  
        # #step13: 检查刷新相关性
        # uds_rep,datasize = self.diag.req_and_res_can([0x31,0x01,0xFF,0x01],4)
        # if uds_rep[0:5] == [0x71,0x01,0xFF,0x01,0x00]:
        #     print('Check programming Dependencies ok')
        # else:
        #     print('Check programming Dependencies fail')
        # #step14: 硬件重启
        # uds_rep,datasize = self.diag.req_and_res_can([0x11,0x01],2)
        # if uds_rep[0:2] == [0x51,0x01]:
        #     print('Hardware reset ok')
        # else:
        #     print('Hardware reset fail')

    def boot_update(self):
        if self.boot_name != '':
            self.app = True
            '''内存擦除前'''
            time.sleep(1)
            #step14: 擦除存储器程序
            self.erashing = True
            time.sleep(0.5)
            '''内存擦除中'''
            try:
                startaddress, size = self.getFile(self.boot_filepath)
                eras_startaddress = [0x00,0x00,0x00,0x00]
                eras_startaddress[0] = ((startaddress)>>24) & 0xFF
                eras_startaddress[1] = ((startaddress)>>16) & 0xFF
                eras_startaddress[2] = ((startaddress)>>8) & 0xFF
                eras_startaddress[3] = (startaddress) & 0xFF
                erash_size = [0x00,0x00,0x00,0x00]
                erash_size[0] = ((size)>>24) & 0xFF
                erash_size[1] = ((size)>>16) & 0xFF
                erash_size[2] = ((size)>>8) & 0xFF
                erash_size[3] = (size) & 0xFF
                uds_rep,_ = self.diag.req_and_res_can([0x31,0x01,0xFF,0x00,0x44]+eras_startaddress + erash_size,13)
                with assume: assert uds_rep[0:5] == [0x71,0x01,0xFF,0x00,0x00]
                if uds_rep[0:5] == [0x71,0x01,0xFF,0x00,0x00]:
                    logging.info('Erase Memory ok')
                else:
                    logging.info('Erase Memory fail')
            except:
                logging.info('Erase Memory no response!')
            time.sleep(1)
            #step15/16/17: 34、36、37服务下载程序
            self.erashing = False
            check_crc_boot = self.download(self.boot_filepath) 
            logging.info(f'check_crc_boot:{check_crc_boot}')  
            #step18: 检查传输行列
            if self.crcerror:
                crc_boot=[0,0,0,0]
            else:
                crc_boot=[0,0,0,0]
                crc_boot[0] = ((check_crc_boot)>>24) & 0xFF
                crc_boot[1] = ((check_crc_boot)>>16) & 0xFF
                crc_boot[2] = ((check_crc_boot)>>8) & 0xFF
                crc_boot[3] = (check_crc_boot) & 0xFF
            try:
                uds_rep,datasize = self.diagfun.req_and_res_can([0x3E,0x80],2)
                uds_rep,datasize = self.diag.req_and_res_can([0x31,0x01,0x02,0x02]+crc_boot,8)
                if uds_rep[0:5] == [0x71,0x01,0x02,0x02,0x00]:
                    logging.info('Check boot transmit sequence ok')
                else:
                    logging.info('Check boot transmit sequence fail')    
            except:
                logging.info('Check boot transmit and programming Dependencies no response!')
                self.finish = True

    def post_update(self):
        try:
            #step20: 检查软硬件一致性
            uds_rep,datasize = self.diag.req_and_res_can([0x31,0x01,0xFF,0x01],4)
            with assume: assert uds_rep[0:5] == [0x71,0x01,0xFF,0x01,0x00]
            if uds_rep[0:5] == [0x71,0x01,0xFF,0x01,0x00]:
                self.success_flag = True
                self.finish = True 
                logging.info('Check programming Dependencies ok')
            else:
                self.success_flag = False
                self.finish = False 
                logging.info('Check programming Dependencies fail')
            #step21: 硬件重启
            uds_rep,datasize = self.diag.req_and_res_can([0x11,0x01],2)
            with assume: assert uds_rep[0:2] == [0x51,0x01]
            if uds_rep[0:2] == [0x51,0x01]:
                logging.info('Hardware reset ok') 
            else:
                self.success_flag = False
                self.finish = False 
                logging.info('Hardware reset fail')

            time.sleep(5)
            '''后刷新步骤'''
            #step14-2: 保持会话   -- 按照软件刷写工具添加
            uds_rep,datasize = self.diagfun.req_and_res_can([0x3E,0x80],2) # diagfun
            #step15: 默认会话模式               :刷写软件是进入扩展回话
            uds_rep,datasize = self.diagfun.req_and_res_can([0x10,0x03],2)  # diagfun
            with assume: assert uds_rep[0:2] == [0x50,0x03]
            if uds_rep[0:2] == [0x50,0x03]:
                print('Entre Extened session ok')
            else:
                print('Entre Eetended session fail')
        
            #step22: 恢复通信
            uds_rep,datasize = self.diag.req_and_res_can([0x19,0x02,0x0B],3)
            uds_rep,datasize = self.diag.req_and_res_can([0x28,0x80,0x01],3)
            self.candriver.stop_FrANFrame_Cycle(self.ab,'CANFD')
            self.vechile_message(True)  # 恢复停发车身报文
            #step16-3: 开启DTC 
            uds_rep,datasize = self.diag.req_and_res_can([0x85,0x81],2)

            uds_rep,datasize = self.diagfun.req_and_res_can([0x10,0x01],2) # diagfun
            with assume: assert uds_rep[0:2] == [0x50,0x01]
            if uds_rep[0:2] == [0x50,0x01]:
                print('Entre default session ok')
            else:
                print('Entre default fail')

            #step17: 清楚故障              ：刷写工具无此项
            uds_rep,datasize = self.diagfun.req_and_res_can([0x14,0xFF,0xFF,0xFF],4) # diagfun
            with assume: assert uds_rep[0:1] == [0x54]
            if uds_rep[0:1] == [0x54]:
                print('Clear DTC ok')
            else:
                print('Clear DTC fail')
            uds_rep,datasize = self.diag.req_and_res_can([0x22,0xF1,0x99],3)
            with assume: assert uds_rep[0:3] == [0x62,0xF1,0x99] 
            if uds_rep == [0x62,0xF1,0x99] + Set_up.ProgrammingDate:
                print('22 read ok')
            else:
                print('22 read fail')
        except:
            self.success_flag = False
            self.finish = False 
            logging.info('After update, CAN Communication Error!')

    def post_abnormal_termination_startup(self):            #刷写失败后验证能否正常启动
        # aa = self.candriver.read_db_signal('ChengTech_77G_V2','CGW_Safety','ICM_DateTime','ICM_CurrentTimeHour')
        # logging.info('read rbs signal value is ',aa)
        # self.normal = True
        for i in range(6):
            uds_rep,datasize = self.diag.req_and_res_can([0x10,0x01],2)
            if len(uds_rep) < 2:
                self.normal =False
            if uds_rep[0:2] == [0x50,0x01]:
                self.normal = True
                break
            else:
                self.normal = False
            time.sleep(1)
        uds_rep,datasize = self.diag.req_and_res_can([0x22,0xF1,0x95],3)
        if uds_rep[0:] == [0x62,0xF1,0x95] + Set_up.ECUSoftwareVersionNumber:
            self.normal = True
        else:
            self.normal = False

    def run(self):
        self.get_path()
        # state = self.check_md5()
        # if state:
        # ab = self.diagfun.send_CANFrame_Cycle(0x7DF,[0x02,0x3E,0x80,0xCC,0xCC,0xCC,0xCC,0xCC],4000,'CANFD')
        self.pre_update()
        if self.skipflash == False:
            self.flash_driver_update()
        self.app_update()
        # self.boot_update()
        self.post_update()
        # # else:
        #     logging.info('MD5 Check fail!')
        # self.diagfun.stop_FrANFrame_Cycle(ab,'CANFD')
        try:
            (dirpath,filename) = os.path.split(self.app_filepath)
            shutil.rmtree(dirpath)
        except:
            logging.info('Delete flash file Error!')
        # self.stop_flag=False                #外部条件停止刷写过程
        # self.md5_state = False              #MD5文件读取比对正常标志
        # self.success_flag = False           #刷写成功标志
        # self.app = False                    #app/flash选择
        # self.normal = False                 #刷写异常后能否正常开机
        # self.erashing = False               #正在擦除内存标志

    def set_stop(self,flag):
        if self.app:
            self.stop_flag = flag

if __name__ == '__main__':
    print('这是主线程：{}'.format(threading.current_thread().name))
    drive_type = 'USE_TS_TC1014_DEVICE'
    configure_baudrate = {'CAN_Type':'CANFD','ArbRate':500,'DataRate':2000,'TermResistor':True}
    '''初始化CAN'''
    project_rbs = TSCAN(drive_type,configure_baudrate)
    project_rbs.connect_application()
    '''初始化UDS'''
    # diag = TSCAN_UDS(ASupportFD = 1,AMaxdlc=15,reqID=0x749,resID=0x7C9,AFctID=0x7DF) # RL 左后
    # diag = TSCAN_UDS(ASupportFD = 1,AMaxdlc=15,reqID=0x74C,resID=0x7CC,AFctID=0x7DF) # RL 386  右后
    diag = TSCAN_UDS(ASupportFD = 1,AMaxdlc=15,reqID=0x748,resID=0x7C8,AFctID=0x7DF) # LF 左前
    # diag = TSCAN_UDS(ASupportFD = 1,AMaxdlc=15,reqID=0x74A,resID=0x7CA,AFctID=0x7DF) # FR 384 pin4和pin10接地
    # diagfun = TSCAN_UDS(ASupportFD = 1,AMaxdlc=15,reqID=0x7DF,resID=0x7CC,AFctID=0x7DF)
    diagfun = TSCAN_UDS(ASupportFD = 1,AMaxdlc=15,reqID=0x7DF,resID=0x7C8,AFctID=0x7DF)
    
    '''选择刷写文件，判断条件，刷写'''
    curPath = os.path.abspath(os.path.dirname(__file__))
    xcd_path = os.path.join(curPath, r'3E80.zip')  #  s000-003,s000-004_ye,S000_004 -199,S000_004,0421
    print(xcd_path)

    # file_path = r'D:\project\2_BAIC_B41V\code\baic_b41v\data\firmware_combine_S000.002.bin'
    # file_path = r'D:\project\2_BAIC_B41V\code\baic_b41v\data\firmware_combine_auto2112.hex'
    
    erash_startaddress = [0x00,0x05,0x00,0x00]
    # erash_size = [0x00,0x07,0xB5,0x72]   # s000.001
    # erash_size = [0x00,0x07,0xC5,0x76]   # s000.002
    # erash_size = [0x00,0x07,0xD4,0xFA]# s000-002_ye
    # erash_size = [0x00,0x07,0xE6,0x7E] # s000-002_ye1
    # erash_size = [0x00,0x07,0xE5,0xFE] # s000-002_ye2
    # erash_size = [0x00,0x07,0xE4,0xFE] # s000-002_0316
    # erash_size = [0x00,0x08,0x29,0x0E] # s000-003
    erash_size = [0x00,0x07,0xFC,0x02] # s000-004
    # erash_size = [0x00,0x07,0xCD,0xF6]  # 20230329
    dl = Download(project_rbs,diag,diagfun,xcd_path,erash_startaddress,erash_size)
    # time.sleep(20)
    # print(dl.progress_number)
    # dl.set_stop(False)
    # logging.info(f'dl:{dl}')
    dl.join()
    # logging.info(f'dl2:{dl}')

    '''关闭CAN'''
    project_rbs.dis_connect_application()
    diag.UDS_can_delete()
    diagfun.UDS_can_delete()